//; ---------------------------------------------------------------------------
//; Object 25 - rings
//; ---------------------------------------------------------------------------

#include "rings.h"

#include "../sprite.h"
#include "../objectsystemNEWER.h"


#define SWAPCOUNT(val) ((val&0xFFFF0000)>>16)+((val&0x0000FFFF)<<16)

static object *obj;

extern byte v_objstate[];  //object state list, used in objectsystem.c

extern word v_lives;              //live counter (or number of lives)
extern word v_rings;              //ring counter
extern byte v_lifecount;	   //live counter value
extern byte f_lifecount;          //live counter update flag
extern byte f_ringcount;          //ring counter update flag

extern byte v_ani1_frame;         //synchronised animation counter 1

//; ---------------------------------------------------------------------------
//; Animation script - ring
//; ---------------------------------------------------------------------------
byte ring[6] = { 5, 4, 5, 6, 7, afRoutine };
byte *Ani_Ring[1] = { ring };

//; ---------------------------------------------------------------------------
//; Sprite mappings - rings
//; ---------------------------------------------------------------------------
byte front[6] = { 1,
                  0xF8, 5, 0, 0, 0xF8
}; //ring front

byte angle1[6] = { 1,
		   0xF8, 5, 0, 4, 0xF8	
}; //ring angle

byte edge[6] = { 1,
                 0xF8, 1, 0, 8, 0xFC
}; //ring perpendicular

byte angle2[6] = {    1,
                   0xF8, 5, 8, 4, 0xF8	
}; //ring angle

byte sparkle1[6] = { 1,
		  0xF8, 5, 0, 0xA, 0xF8	
}; //sparkle

byte sparkle2[6] = { 1,
		  0xF8, 5, 0x18, 0xA, 0xF8 
}; //sparkle

byte sparkle3[6] = { 1,
		  0xF8, 5, 8, 0xA, 0xF8	
}; //sparkle

byte sparkle4[6] = { 1,
		  0xF8, 5, 0x10, 0xA, 0xF8 
}; //sparkle
		
byte *Map_Ring[8] = {   front,   angle1,     edge,   angle2,
                     sparkle1, sparkle2, sparkle3, sparkle4  };

//==========
void (*Ring_Index[5])() = { Ring_Main, Ring_Animate, Ring_Collect, Ring_Sparkle, Ring_Delete };

//Rings:					; XREF: Obj_Index
void Rings(object *_obj)
{
    obj = _obj;

    //moveq	#0,d0
    //move.b	obRoutine(a0),d0
    byte routine = obj->Routine/2;

    //move.w	Ring_Index(pc,d0.w),d1
    //jmp	Ring_Index(pc,d1.w)
    (*Ring_Index[routine])();
}

//; ===========================================================================

/*Ring_Index:
ptr_Ring_Main:		dc.w Ring_Main-Ring_Index
ptr_Ring_Animate:	dc.w Ring_Animate-Ring_Index
ptr_Ring_Collect:	dc.w Ring_Collect-Ring_Index
ptr_Ring_Sparkle:	dc.w Ring_Sparkle-Ring_Index
ptr_Ring_Delete:	dc.w Ring_Delete-Ring_Index

id_Ring_Main:		= ptr_Ring_Main-Ring_Index	; 0
id_Ring_Animate:	= ptr_Ring_Animate-Ring_Index	; 2
id_Ring_Collect:	= ptr_Ring_Collect-Ring_Index	; 4
id_Ring_Sparkle:	= ptr_Ring_Sparkle-Ring_Index	; 6
id_Ring_Delete:		= ptr_Ring_Delete-Ring_Index	; 8*/

//; ---------------------------------------------------------------------------
//; Distances between rings (format: horizontal, vertical)
//; ---------------------------------------------------------------------------
/*Ring_PosData:	dc.b $10, 0		; horizontal tight
		dc.b $18, 0		; horizontal normal
		dc.b $20, 0		; horizontal wide
		dc.b 0,	$10		; vertical tight
		dc.b 0,	$18		; vertical normal
		dc.b 0,	$20		; vertical wide
		dc.b $10, $10		; diagonal
		dc.b $18, $18
		dc.b $20, $20
		dc.b $F0, $10
		dc.b $E8, $18
		dc.b $E0, $20
		dc.b $10, 8
		dc.b $18, $10
		dc.b $F0, 8
		dc.b $E8
; ===========================================================================*/

byte Ring_PosData[32] = { 0x10,    0, 0x18,    0, 0x20,    0,
                             0, 0x10,    0, 0x18,    0, 0x20,
                          0x10, 0x10, 0x18, 0x18, 0x20, 0x20,
                          0xF0, 0x10, 0xE8, 0x18, 0xE0, 0x20,
                          0x10,    8, 0x18, 0x10,
                          0xF0,    8, 0xE8, 0x10
};


//Ring_Main:	; Routine 0
void Ring_Main()
{
    //lea	(v_objstate).w,a2
    byte *statePtr = v_objstate;

    //moveq	#0,d0
    //move.b	obRespawnNo(a0),d0
    byte respawn = obj->RespawnNo;

    //lea	2(a2,d0.w),a2
    byte *ringID = &statePtr[2+respawn];      //does state list work correctly? counts show but not object id's..?

    //move.b	(a2),d4
    byte _ringID = (*ringID);

    //move.b	obSubtype(a0),d1
    byte subtype = obj->Subtype;

    //move.b	d1,d0
    byte _subtype = subtype;

    //andi.w	#7,d1
    word groupCount = subtype & 7;

    //cmpi.w	#7,d1
    //bne.s	loc_9B80
    //moveq	#6,d1
    if (groupCount == 7)
        groupCount = 6;
		

    //loc_9B80:
    //swap	d1
    //move.w #0, d1
    long count = SWAPCOUNT(groupCount);

    //lsr.b	#4,d0
    //add.w	d0,d0
    byte subtypeOffset = ((byte)(_subtype / 16) * 2);

    //move.b	Ring_PosData(pc,d0.w),d5 ; load ring spacing data
    //ext.w	d5
    signed short spaceX = Ring_PosData[subtypeOffset];

    //move.b	Ring_PosData+1(pc,d0.w),d6
    //ext.w	d6
    signed short spaceY = Ring_PosData[subtypeOffset+1];

    //movea.l	a0,a1
    object *ringObj = obj;

    //move.w	obX(a0),d2
    word ringGroupX = obj->X;

    //move.w	obY(a0),d3
    word ringGroupY = obj->Y;

    #define ringX ringGroupX
    #define ringY ringGroupY
    RingGroup_MakeFirstRing(count, ringObj, ringID, _ringID, ringX, ringY, spaceX, spaceY);
    
    //Ring_MakeRings:
    byte freeRing=0;
    while ( ((signed short)(count&0xFFFF) >= 0) && (freeRing == 0) )
    {
        //swap	d1
        count = SWAPCOUNT(count);   //swaps count from count-down to count-up portion

        //
        freeRing = RingGroup_MakeAdditionalRing(count, ringObj, ringID, _ringID, ringX, ringY, spaceX, spaceY);
        
        
        count = count - 1;
 
    }
    //dbf	d1,Ring_MakeRings ; repeat for	number of rings

    
    //loc_9C0E:
    //btst	#0,(a2)
    //bne.w	DeleteObject
    if ((*ringID & 1) == 1)
        DeleteObject(ringObj); 
    else
        Ring_Animate();

}

//-pops next bit off of 8 bit ring flag list (ID)
//   if 0: new sub-ring object initialized in place of ring 'group' object, at position (ringX, ringY) with sub-ID count (8th bit on left cleared as well)
//   if 1: new sub-ring object does not replace original ring 'group' object
//-(ringX, ringY) incremented with proper spacing and count incremented for eventual init of next sub-ring
//   (this includes swapping 'count' counter to count-down portion)
//-count 'count-down' portion decremented for start loop one less in MakeRings loop

void RingGroup_MakeFirstRing(long &count, object *ringObj, byte *IDflag, byte &ID, word &ringX, word &ringY, signed short spaceX, signed short spaceY)
{
    byte shiftOut = ID % 2;
    //lsr.b	#1,d4
    ID = ID >> 1;

    //bcs.s	loc_9C02
    if (shiftOut != 1)
    {
        //bclr	#7,(a2)
        *IDflag = *IDflag - (*IDflag & 0x80);   //clear bit 7 in obj state list for this ring
 
	RingGroup_InitSingleRing(count, ringObj, ringObj, ringX, ringY);
    }
    
    //loc_9C02:
    RingGroup_NextRingAddSpace(count, ringX, ringY, spaceX, spaceY);

    count = count - 1;

}

//-pops next bit off of 8 bit ring flag list (ID)
//   if 0: 8th bit on left cleared
//         find the next free object
//         if object is free: new sub-ring object initialized in place of free object, at position (ringX, ringY) with sub-ID count 
//   if 1: new sub-ring object does not need initialization
//-(ringX, ringY) incremented with proper spacing and count incremented for eventual init of next sub-ring
//  (this includes swapping 'count' counter to count-down portion)
byte RingGroup_MakeAdditionalRing(long &count, object *ringGroupObj, byte *IDflag, byte &ID, word &ringX, word &ringY, signed short spaceX, signed short spaceY)
{

    object *ringObj;

    byte shiftOut = ID % 2;
    //lsr.b	#1,d4
    ID = ID >> 1;
		
    //bcs.s	loc_9C02
    if (shiftOut != 1)
    {
        //bclr	#7,(a2)
        *IDflag = *IDflag - (*IDflag & 0x80);   //clear bit 7 in obj state list for this ring
 
        //bsr.w	FindFreeObj
        ringObj = FindFreeObj();

        if (ringObj->ID == 0)
        {
            
	    RingGroup_InitSingleRing(count, ringGroupObj, ringObj, ringX, ringY);
            RingGroup_NextRingAddSpace(count, ringX, ringY, spaceX, spaceY);
            return 0;
        }
    }
    else
        RingGroup_NextRingAddSpace(count, ringX, ringY, spaceX, spaceY);
        
    return 0;
}

		
//initializes parameters of sub-ring object newRingObj
//  -using oldRingObj as the parent ring (group) object
//  -at position (ringX, ringY)
//  -with sub-ID count 
void RingGroup_InitSingleRing(long count, object *oldRingObj, object *newRingObj, word ringX, word ringY)
{

    //; ===========================================================================

    //loc_9BBA:				; XREF: Ring_Main
    //move.b	#id_Rings,0(a1)	; load ring object
    newRingObj->ID = id_Rings;
 
    //addq.b	#2,obRoutine(a1)
    newRingObj->Routine = newRingObj->Routine + 2;

    //move.w	d2,obX(a1)	; set x-axis position based on d2
    newRingObj->X = ringX;

    //move.w	obX(a0),$32(a1)
    ptrsetobjvariableword(0x32,newRingObj,oldRingObj->X)

    //move.w	d3,obY(a1)	; set y-axis position based on d3
    newRingObj->Y = ringY;

    //move.l	#Map_Ring,obMap(a1)
    newRingObj->Map = Map_Ring;

    //move.w	#$27B2,obGfx(a1)
    newRingObj->Gfx = 0x27B2;

    //move.b	#4,obRender(a1) 
    newRingObj->Render = 4;

    //move.b	#2,obPriority(a1)
    newRingObj->Priority = 2;

    //move.b	#$47,obColType(a1)
    newRingObj->ColType = 0x47;

    //move.b	#8,obActWid(a1)
    newRingObj->ActionWidth = 8;

    //move.b	obRespawnNo(a0),obRespawnNo(a1)
    newRingObj->RespawnNo = oldRingObj->RespawnNo;

    //move.b	d1,$34(a1)
    newRingObj->Variables[0x34-0x28] = count;

    
}

//increments ID portion of ring counter count for next sub-ring
//  -adds spaceX to ringX to compute the X position for next sub-ring
//  -adds spaceY to ringY to compute the Y position for next sub-ring
//  -swaps 'count' counter to count-down portion
void RingGroup_NextRingAddSpace(long &count, word &ringX, word &ringY, word spaceX, word spaceY)
{
    //loc_9C02:
    //addq.w	#1,d1
    count = count+1;

    //add.w	d5,d2		; add ring spacing value to d2
    ringX = ringX + spaceX;

    //add.w	d6,d3		; add ring spacing value to d3 
    ringY = ringY + spaceY;

    //swap	d1
    count = SWAPCOUNT(count);

}   



//Ring_Animate:	; Routine 2
void Ring_Animate()
{
    //move.b	(v_ani1_frame).w,obFrame(a0) ; set frame
    obj->Frame = v_ani1_frame;

    //bsr.w	DisplaySprite
    DisplaySprite(obj);

    //out_of_range.s	Ring_Delete,$32(a0)
    if ( out_of_range(ptrgetobjvariableword(0x32,obj)) ) { Ring_Delete(); }
}   //rts	
//; ===========================================================================

//Ring_Collect:	; Routine 4
void Ring_Collect()
{
    //addq.b	#2,obRoutine(a0)
    obj->Routine = obj->Routine + 2;

    //move.b	#0,obColType(a0)
    obj->ColType = 0;

    //move.b	#1,obPriority(a0)
    obj->Priority = 1;

    //bsr.w	CollectRing
    CollectRing();

    //lea	(v_objstate).w,a2
    byte *statePtr = v_objstate;

    //moveq	#0,d0
    //move.b	obRespawnNo(a0),d0
    byte respawn = obj->RespawnNo;

    //move.b	$34(a0),d1
    byte ringBit = obj->Variables[0x34-0x28];

    //bset	d1,2(a2,d0.w)
    statePtr[2+respawn] = statePtr[2+respawn] | (1<<ringBit);
 
    Ring_Sparkle();
}

//Ring_Sparkle:	; Routine 6
void Ring_Sparkle()
{
    //lea	(Ani_Ring).l,a1
    byte **ringAnim = Ani_Ring;

    //bsr.w	AnimateSprite
    AnimateSprite(obj, ringAnim);

    //bra.w	DisplaySprite
    DisplaySprite(obj);
}

//; ===========================================================================

//Ring_Delete:	; Routine 8
void Ring_Delete()
{
    //bra.w	DeleteObject
    DeleteObject(obj);
}

//; ||||||||||||||| S U B	R O U T	I N E |||||||||||||||||||||||||||||||||||||||


//CollectRing:				; XREF: Ring_Collect
void CollectRing()
{
    //addq.w	#1,(v_rings).w	; add 1 to rings
    v_rings = v_rings + 1;

    //ori.b	#1,(f_ringcount).w ; update the rings counter
    f_ringcount = f_ringcount | 1;

    //move.w	#sfx_Ring,d0	; play ring sound
    
    //cmpi.w	#100,(v_rings).w ; do you have < 100 rings?
    //bcs.s	@playsnd	; if yes, branch
    if (v_rings >= 100)
    {
        byte lifecountbit2 = v_lifecount & 2;
        //bset	#1,(v_lifecount).w ; update lives counter
        v_lifecount = v_lifecount | 2;

	//beq.s	@got100
        if (lifecountbit2 != 0)
        {
	    //cmpi.w	#200,(v_rings).w ; do you have < 200 rings?
            //bcs.s	@playsnd	; if yes, branch
            if (v_rings >= 200)
            {
                lifecountbit2 = v_lifecount & 2;
		//bset	#2,(v_lifecount).w ; update lives counter
                v_lifecount = v_lifecount | 2;

		//bne.s	@playsnd
                if (lifecountbit2 == 0)
                {
                    //@got100:
		    //addq.b	#1,(v_lives).w	; add 1 to the number of lives you have
		    v_lives = v_lives + 1;

                    //addq.b	#1,(f_lifecount).w ; update the lives counter
                    f_lifecount = f_lifecount + 1;

		    //move.w	#bgm_ExtraLife,d0 ; play extra life music
                }
            }
        }
        else
        {
            //@got100:
            //addq.b	#1,(v_lives).w	; add 1 to the number of lives you have
	    v_lives = v_lives + 1;

            //addq.b	#1,(f_lifecount).w ; update the lives counter
            f_lifecount = f_lifecount + 1;

	    //move.w	#bgm_ExtraLife,d0 ; play extra life music
        }
    }
 
    //@playsnd:
    //jmp	(PlaySound_Special).l

}   //; End of function CollectRing


//===========================================================================
#include "../misc.h"

//; ---------------------------------------------------------------------------
//; Object 37 - rings flying out of Sonic	when he's hit
//; ---------------------------------------------------------------------------

extern byte v_ani3_time;
extern byte v_ani3_frame;
extern word v_limitbtm2;

void (*RLoss_Index[5])() = { RLoss_Count, RLoss_Bounce, RLoss_Collect, RLoss_Sparkle, RLoss_Delete };

//RingLoss:				; XREF: Obj_Index
void RingLoss(object *_obj)
{
    obj = _obj;

    //moveq	#0,d0
    //move.b	obRoutine(a0),d0
    byte routine = obj->Routine/2;

    //move.w	RLoss_Index(pc,d0.w),d1
    //jmp	RLoss_Index(pc,d1.w)
    (*RLoss_Index[routine])();
}

//; ===========================================================================

//RLoss_Count:	; Routine 0
void RLoss_Count()
{
    //movea.l	a0,a1
    object *subRing = obj;  //first sub-ring refers to PARENT object

    //moveq	#0,d5
    //move.w	(v_rings).w,d5	; check number of rings you have
    signed short rings = v_rings;

    //moveq	#32,d0
    long maxLoss = 32;

    //cmp.w	d0,d5		; do you have 32 or more?
    //bcs.s	@belowmax	; if not, branch
    if (rings > maxLoss)
    {
        //move.w	d0,d5		; if yes, set d5 to 32
        rings = maxLoss;
    }

    //@belowmax:
    //subq.w	#1,d5
    rings = rings - 1;

    //move.w	#$288,d4
    signed short ringAngle = 0x288;

    //bra.s	@makerings
    //; ===========================================================================

    for (;rings>=0;rings--)
    {	

        //@makerings:
        //move.b	#id_RingLoss,0(a1) ; load bouncing ring object
        subRing->ID = id_RingLoss;

        //addq.b	#2,obRoutine(a1)
        subRing->Routine = subRing->Routine + 2;

        //move.b	#8,obHeight(a1)
        subRing->Height = 8;

        //move.b	#8,obWidth(a1)
        subRing->Width = 8;

        //move.w	obX(a0),obX(a1)
        subRing->X = obj->X;

        //move.w	obY(a0),obY(a1)
        subRing->Y = obj->Y;

        //move.l	#Map_Ring,obMap(a1)
        subRing->Map = Map_Ring;

        //move.w	#$27B2,obGfx(a1)
        subRing->Gfx = 0x27B2;

        //move.b	#4,obRender(a1)
        subRing->Render = 4;

        //move.b	#3,obPriority(a1)
        subRing->Priority = 3;

        //move.b	#$47,obColType(a1)
        subRing->ColType = 0x47;

        //move.b	#8,obActWid(a1)
        subRing->ActionWidth = 8;

        //move.b	#-1,(v_ani3_time).w
        v_ani3_time = -1;

        signed short scaledSine, scaledCosine;
        //tst.w	d4
        //bmi.s	@loc_9D62
        if (ringAngle >= 0)
        {
            //move.w	d4,d0
            signed short angle = ringAngle;
    
            signed short sine, cosine;
            //bsr.w	CalcSine       
            CalcSine(ringAngle, sine, cosine);
    
            //move.w	d4,d2
            //lsr.w	#8,d2
            signed short anglePower = ringAngle/256;
    
	    //asl.w	d2,d0
            //asl.w	d2,d1
            //move.w	d0,d2
            //move.w	d1,d3
            scaledSine = sine << anglePower;
            scaledCosine = cosine << anglePower;
	
	    //addi.b	#$10,d4
            ringAngle = ringAngle + 0x10;

	    //bcc.s	@loc_9D62
            if ((ringAngle&0xFF) >= 0x80)
            {
                //subi.w	#$80,d4
                ringAngle = ringAngle - 0x80;
                
	        //bcc.s	@loc_9D62
                if (ringAngle < 0) 
	        {
                    //move.w	#$288,d4
                    ringAngle = 0x288;
                }
	    }
        }
        
        //@loc_9D62:
        //move.w	d2,obVelX(a1)
        subRing->VelocityX = scaledSine;

        //move.w	d3,obVelY(a1)
        subRing->VelocityY = scaledCosine;

	//neg.w	d2
        scaledSine = -scaledSine;
    
	//neg.w	d4
        ringAngle = -ringAngle;

	//dbf	d5,@loop	; repeat for number of rings (max 31)
        //@loop:
        if (rings > 0)
        {
	    //bsr.w	FindFreeObj
            subRing = FindFreeObj();

	    //bne.w	@resetcounter
            if (subRing->ID != 0) { break; }
        }
    }

    //@resetcounter:
    //move.w	#0,(v_rings).w	; reset number of rings to zero
    v_rings = 0;
	
    //move.b	#$80,(f_ringcount).w ; update ring counter
    f_ringcount = 0x80;
	
    //move.b	#0,(v_lifecount).w
    v_lifecount = 0;
	
    //sfx	sfx_RingLoss	; play ring loss sound
   
    RLoss_Bounce();
}

//RLoss_Bounce:	; Routine 2
void RLoss_Bounce()
{
    //move.b	(v_ani3_frame).w,obFrame(a0)
    obj->Frame = v_ani3_frame;
	
    //bsr.w	SpeedToPos
    SpeedToPos(obj);

    //addi.w	#$18,obVelY(a0)
    obj->VelocityY = obj->VelocityY + 0x18;

    //bmi.s	@chkdel
    if (obj->VelocityY >= 0)
    {
        //move.b	(v_vbla_byte).w,d0
        
		//add.b	d7,d0
		//andi.b	#3,d0
		//bne.s	@chkdel

	//jsr	ObjFloorDist
        signed short floorDist = ObjFloorDist(obj);

	//tst.w	d1
        //bpl.s	@chkdel
        if (floorDist < 0)
        {
	    //add.w	d1,obY(a0)
            obj->Y = obj->Y + floorDist;

	    //move.w	obVelY(a0),d0
            word ringFall = obj->VelocityY;
 
	    //asr.w	#2,d0
            word scaledRingFall = ringFall/4;

	    //sub.w	d0,obVelY(a0)
            obj->VelocityY = obj->VelocityY - scaledRingFall;

	    //neg.w	obVelY(a0)
            obj->VelocityY = -obj->VelocityY;
        }
    }
 
    //@chkdel:
    //tst.b	(v_ani3_time).w
    //beq.s	RLoss_Delete
    if (v_ani3_time == 0) { RLoss_Delete(); }
    else
    {
        //move.w	(v_limitbtm2).w,d0
        //addi.w	#$E0,d0
        word limitbtm = v_limitbtm2 + 0xE0;

	//cmp.w	obY(a0),d0	; has object moved below level boundary?
        //bcs.s	RLoss_Delete	; if yes, branch
        if (limitbtm > obj->Y)
	{
            //bra.w	DisplaySprite
            DisplaySprite(obj);
        }
        else
            RLoss_Delete();
    }
}
        
//; ===========================================================================

//RLoss_Collect:	; Routine 4
void RLoss_Collect()
{
    //addq.b	#2,obRoutine(a0)
    obj->Routine = obj->Routine + 2;

    //move.b	#0,obColType(a0)
    obj->ColType = 0;

    //move.b	#1,obPriority(a0)
    obj->Priority = 1;

    //bsr.w	CollectRing
    CollectRing();

    RLoss_Sparkle();
}

//RLoss_Sparkle:	; Routine 6
void RLoss_Sparkle()
{
    //lea	(Ani_Ring).l,a1
     byte **ringAnim = Ani_Ring;
	
    //bsr.w	AnimateSprite
    AnimateSprite(obj, ringAnim);

    //bra.w	DisplaySprite
    DisplaySprite(obj);

}   //; ===========================================================================

//RLoss_Delete:	; Routine 8
void RLoss_Delete()
{
    //bra.w	DeleteObject
    DeleteObject(obj);
}                 
